// Learn TypeScript:
//  - https://docs.cocos.com/creator/manual/en/scripting/typescript.html
// Learn Attribute:
//  - https://docs.cocos.com/creator/manual/en/scripting/reference/attributes.html
// Learn life-cycle callbacks:
//  - https://docs.cocos.com/creator/manual/en/scripting/life-cycle-callbacks.html

import GameWorld from "./entity/GameWorld";
import Obstacle from "./entity/Obstacle";
import Vehicle from "./entity/Vehicle";

const { ccclass, property } = cc._decorator;
let offsetPursuit = 50;
@ccclass
export default class SteeringCtrl extends cc.Component {

    @property(cc.Prefab)
    vehiclePrefab: cc.Prefab = null;

    @property(cc.Node)
    touchNode: cc.Node = null;

    @property(cc.Node)
    gameNode: cc.Node = null;

    @property(cc.Graphics)
    pen: cc.Graphics = null;

    @property(cc.Graphics)
    flowPen: cc.Graphics = null;

    @property(cc.Prefab)
    obstaclePrefab: cc.Prefab = null;

    @property(cc.Label)
    desc: cc.Label = null;

    start() {
        this._registerTouch();
        this.pen.fillColor = cc.Color.RED;
        this._createWorld();
        this._curTestIndex = 0;
        this.reset();
    }

    private _registerTouch() {
        this.touchNode.on(cc.Node.EventType.TOUCH_START, this._handleTouchStart, this);
        this.touchNode.on(cc.Node.EventType.TOUCH_MOVE, this._handleTouchMove, this);
        this.touchNode.on(cc.Node.EventType.TOUCH_END, this._handleTouchEnd, this);
        this.touchNode.on(cc.Node.EventType.TOUCH_CANCEL, this._handleTouchEnd, this);
    }
    private _handleTouchStart(event: cc.Touch) {

    }

    private _curTestIndex: number = 0;
    public testList = [
        {
            func: this.arriveTest,
            desc: "到达某个点,点击屏幕将到达对应位置停下"
        },
        {
            func: this.seekTest,
            desc: "寻找,点击屏幕将不断寻找到对应位置"
        },
        {
            func: this.fleeTest,
            desc: "距离目标特定距离后开始远离某个目标点，将远离点击屏幕的位置"
        },
        {
            func: this.wanderTest,
            desc: "徘徊"
        },
        {
            func: this.pursuitTest,
            desc: "追逐目标，根据目标速度预测最终寻找的位置"
        },
        {
            func: this.evadeTest,
            desc: "躲避目标，跟追逐相反"
        },
        {
            func: this.interposeTest,
            desc: "移动到两个目标中心"
        },
        {
            func: this.obstacleAvoidanceTest,
            desc: "躲避圆形障碍物"
        },
        {
            func: this.followPathTest,
            desc: "简单的路径跟随"
        },
        {
            func: this.offsetPursuitTest,
            desc: "偏移追逐，设定追逐目标偏移，实现战斗队列等移动行为"
        },
        {
            func: this.flockingTest,
            desc: "群集，分散，聚拢，队列（对齐方向）用于简单模拟鱼群，蚁群等群体运动之间的关系"
        },
        {
            func: this.flowTest,
            desc: "根据流场方向移动"
        },
    ];

    private _handleTouchMove(event: cc.Touch) {

    }
    private _targetPos: cc.Vec2 = cc.v2();
    private _handleTouchEnd(event: cc.Touch) {
        let delta = event.getLocation();
        let vec = this.gameNode.convertToNodeSpaceAR(delta);
        cc.Vec2.set(this._targetPos, vec.x, vec.y);
        if (this._testVehicle)
            this._testVehicle.steering.setTarget(vec);
        if (this._world && this._testVehicle && this._testVehicle.steering.isFlowOn()) {
            let rowandcol = this._world.getColAndRowByPosition(vec.x, vec.y);
            this._world.generateFlowField(rowandcol[0], rowandcol[1]);
            this.flowPen.clear();
            this._world.render(this.flowPen);
            for(let i = 0, len = this._world.agents.length; i<len;i++){
                this._world.agents[i].steering.setTarget(this._targetPos);
            }
        }
    }

    private _world: GameWorld = null;
    private _createWorld() {
        this._world = new GameWorld(-cc.winSize.width * 0.5, -cc.winSize.height * 0.5, cc.winSize.width, cc.winSize.height);
    }

    private _testVehicle: Vehicle = null;
    //到达
    private arriveTest() {
        this.gameNode.destroyAllChildren();
        let vehicle = this.createVehicle();
        this._testVehicle = vehicle
        this._testVehicle.steering.arriveOn();
        this._targetPos.set(cc.Vec2.ZERO);
        this._testVehicle.steering.setTarget(this._targetPos);
    }

    //寻找
    private seekTest() {
        this.gameNode.destroyAllChildren();
        let vehicle = this.createVehicle();
        this._testVehicle = vehicle;
        this._testVehicle.steering.seekOn();
        this._targetPos.set(cc.Vec2.ZERO);
        this._testVehicle.steering.setTarget(this._targetPos);
    }

    //远离
    private fleeTest() {
        this.gameNode.destroyAllChildren();
        let vehicle = this.createVehicle();
        this._testVehicle = vehicle
        this._testVehicle.steering.fleeOn();
        this._targetPos.set(cc.Vec2.ZERO);
        this._testVehicle.steering.setTarget(this._targetPos);
    }

    //徘徊
    private wanderTest() {
        this.gameNode.destroyAllChildren();
        let vehicle = this.createVehicle();
        this._testVehicle = vehicle;
        this._testVehicle.steering.wanderOn();
    }

    private _pursuitVehicle: Vehicle = null;
    //追逐
    private pursuitTest() {
        this.gameNode.destroyAllChildren();
        let vehicle = this.createVehicle();
        this._testVehicle = vehicle;
        this._testVehicle.steering.arriveOn();
        this._targetPos.set(cc.Vec2.ZERO);
        this._testVehicle.steering.setTarget(this._targetPos);
        vehicle.maxSpeed = 180;
        let vehicle2 = this.createVehicle();
        vehicle2.node.setPosition(300, 300);
        vehicle2.maxSpeed = 150;
        vehicle2.steering.pursuitOn(vehicle);
        this._pursuitVehicle = vehicle2;
    }

    //躲避
    private evadeTest() {
        this.gameNode.destroyAllChildren();
        let vehicle = this.createVehicle();
        this._testVehicle = vehicle;
        this._testVehicle.steering.arriveOn();
        this._targetPos.set(cc.Vec2.ZERO);
        this._testVehicle.steering.setTarget(this._targetPos);
        // vehicle.maxSpeed = 180;
        let vehicle2 = this.createVehicle();
        vehicle2.node.setPosition(100, 100);
        // vehicle2.maxSpeed = 150;
        vehicle2.steering.evadeOn(vehicle);
        this._pursuitVehicle = vehicle2;
    }

    //插入
    private interposeTest() {
        this.gameNode.destroyAllChildren();
        let vehicle2 = this.createVehicle();
        vehicle2.node.setPosition(Math.random() * 300, Math.random() * (-300));
        let vehicle3 = this.createVehicle();
        vehicle3.node.setPosition(Math.random() * (-300), Math.random() * (300));
        vehicle2.steering.pursuitOn(vehicle3);
        vehicle3.steering.pursuitOn(vehicle2);
        this._testVehicle = null;
        let vehicle = this.createVehicle();
        vehicle.steering.interposeOn(vehicle2, vehicle3);
    }

    //躲避圆形碰撞体
    private obstacleAvoidanceTest() {
        this.gameNode.destroyAllChildren();
        let vehicle = this.createVehicle();
        this._testVehicle = vehicle;
        this._testVehicle.steering.arriveOn();
        this._testVehicle.steering.obstacleAvoidanceOn();
        this._targetPos.set(cc.Vec2.ZERO);
        this._testVehicle.steering.setTarget(this._targetPos);
        this._world.obstacles.length = 0;
        for (let i = 0; i < 5; i++) {
            let node = cc.instantiate(this.obstaclePrefab);
            node.parent = this.gameNode;
            node.setPosition(-Math.random() * cc.winSize.width + cc.winSize.width * 0.5, -Math.random() * cc.winSize.height + cc.winSize.height * 0.5);
            let ob = node.getComponent(Obstacle);
            ob.radius = Math.random() * 10 + 30;
            node.width = ob.radius * 2;
            node.height = ob.radius * 2;
            this._world.obstacles.push(ob);
        }
    }

    //简单路径跟随
    private followPathTest() {
        this.gameNode.destroyAllChildren();
        let vehicle = this.createVehicle();
        this._testVehicle = vehicle;
        this._testVehicle.steering.followPathOn();
    }

    private _PosList = [//队列偏移值
        // [0, -offsetpet],
        [-offsetPursuit / 2, -offsetPursuit * 1], [offsetPursuit / 2, -offsetPursuit * 1],
        [-offsetPursuit, -offsetPursuit * 2], [0, -offsetPursuit * 2], [offsetPursuit, -offsetPursuit * 2],
        [-offsetPursuit * 3 / 2, -offsetPursuit * 3], [-offsetPursuit / 2, -offsetPursuit * 3], [offsetPursuit / 2, -offsetPursuit * 3], [offsetPursuit * 3 / 2, -offsetPursuit * 3]
    ];
    //偏移追逐
    private offsetPursuitTest() {
        this.gameNode.destroyAllChildren();
        let vehicle = this.createVehicle();
        this._testVehicle = vehicle;
        this._testVehicle.steering.arriveOn();
        for (let i = 0, len = this._PosList.length; i < len; i++) {
            vehicle = this.createVehicle();
            let offset = this._PosList[i];
            vehicle.node.setPosition(this._testVehicle.position.x + offset[0], this._testVehicle.position.y + offset[1]);
            let dis = cc.v2(offset[0], offset[1]);
            vehicle.steering.offsetPursuitOn(this._testVehicle, dis);
        }
    }

    //集群
    private flockingTest() {
        this.gameNode.destroyAllChildren();
        this._world.agents.length = 0;
        for (let i = 0, len = 30; i < len; i++) {
            let vehicle = this.createVehicle();
            vehicle.node.setPosition(-Math.random() * cc.winSize.width + cc.winSize.width * 0.5, -Math.random() * cc.winSize.height + cc.winSize.height * 0.5);
            if (i == 0) {
                vehicle.maxSpeed = 70;
                vehicle.node.children[0].color = cc.Color.RED;
                vehicle.steering.wanderOn();
                this._testVehicle = vehicle;
                this._world.agents.push(vehicle);
            } else {
                vehicle.steering.flockingOn();
                vehicle.steering.wanderOn();
                vehicle.steering.evadeOn(this._testVehicle);
                this._world.agents.push(vehicle);

                vehicle.isTag = i % 2 == 0;
            }
        }
    }
    //流场
    private flowTest() {
        let rowandcol = this._world.getColAndRowByPosition(0, 0);
        this._world.generateFlowField(rowandcol[0], rowandcol[1]);
        this.flowPen.clear();
        this._world.render(this.flowPen);
        this.gameNode.removeAllChildren();
        this._world.agents.length = 0;
        for (let i = 0, len = 30; i < len; i++) {
            let vehicle = this.createVehicle();
            vehicle.node.setPosition(-Math.random() * cc.winSize.width + cc.winSize.width * 0.5, -Math.random() * cc.winSize.height + cc.winSize.height * 0.5);
            vehicle.steering.flowOn();
            vehicle.steering.arriveOn();
            // vehicle.steering.separationOn();
            this._targetPos.set(cc.Vec2.ZERO);
            vehicle.steering.setTarget(this._targetPos);
            vehicle.maxSpeed = 80;
            // this._world.agents.push(vehicle);
            // vehicle.isTag = i % 2 == 0;
            if (i == 0)
                this._testVehicle = vehicle;
        }

    }

    private reset() {
        let test = this.testList[this._curTestIndex];
        test.func.call(this);
        this.desc.string = test.desc;
    }

    private tonext() {
        if (this._curTestIndex++ >= this.testList.length) {
            this._curTestIndex = 0;
        }
        this.reset();
    }


    public createVehicle() {
        let node = cc.instantiate(this.vehiclePrefab);
        node.parent = this.gameNode;
        let vehicle = node.getComponent(Vehicle);
        vehicle.setWorld(this._world);
        vehicle.init();
        vehicle.steering.debugPen = this.pen;
        node.setPosition(0, 0);
        return vehicle;
    }

    protected update(dt: number): void {
        if (this._world.isDebug) {
            this.pen.clear();
        }
    }
}
